home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / misc / gs261src.zip / isave.c < prev    next >
C/C++ Source or Header  |  1993-05-25  |  18KB  |  577 lines

  1. /* Copyright (C) 1991, 1992, 1993 Aladdin Enterprises.  All rights reserved.
  2.  
  3. This file is part of Ghostscript.
  4.  
  5. Ghostscript is distributed in the hope that it will be useful, but
  6. WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
  7. to anyone for the consequences of using it or for whether it serves any
  8. particular purpose or works at all, unless he says so in writing.  Refer
  9. to the Ghostscript General Public License for full details.
  10.  
  11. Everyone is granted permission to copy, modify and redistribute
  12. Ghostscript, but only under the conditions described in the Ghostscript
  13. General Public License.  A copy of this license is supposed to have been
  14. given to you along with Ghostscript so you can know your rights and
  15. responsibilities.  It should be in a file named COPYING.  Among other
  16. things, the copyright notice and this notice must be preserved on all
  17. copies.  */
  18.  
  19. /* isave.c */
  20. /* Save/restore machinery for Ghostscript */
  21. #include "ghost.h"
  22. #include "memory_.h"
  23. #include "alloc.h"
  24. #include "astate.h"
  25. #include "errors.h"
  26. #include "iname.h"
  27. #include "packed.h"
  28. #include "save.h"
  29. #include "store.h"            /* for ref_assign */
  30.  
  31. /* Imported restore routines */
  32. extern void file_save(P0());
  33. extern void file_restore(P1(alloc_save *));
  34. extern void font_restore(P1(alloc_save *));
  35.  
  36. /*
  37.  * The logic for saving and restore the state is rather subtle.
  38.  * Both the changes to individual objects, and the overall state
  39.  * of the memory manager, must be saved and restored.
  40.  */
  41.  
  42. /*
  43.  * To save the state of the memory manager:
  44.  *    Save the state of the current chunk in which we are allocating.
  45.  *    Save the identity of the current chunk.
  46.  *    Save and reset the malloc chain and the orphan block chains.
  47.  * By doing this, we guarantee that no object older than the save
  48.  * can be freed.
  49.  *
  50.  * To restore the state of the memory manager:
  51.  *    Free all chunks newer than the save.
  52.  *    Free all malloc'ed blocks newer than the save.
  53.  *    Make current the chunk that was current at the time of the save.
  54.  *    Free all objects allocated in the current chunk since the save.
  55.  */
  56.  
  57. /*
  58.  * For saving changes to individual objects, we add an "attribute" bit
  59.  * (l_new) that logically belongs to the slot where the descriptor is stored,
  60.  * not to the descriptor itself.  The bit means "the contents
  61.  * of this slot have been changed since the last save."
  62.  * To keep track of changes since the save, we associate a chain of
  63.  * <slot, old_contents> pairs that remembers the old contents of slots.
  64.  *
  65.  * When creating an object, if the save level is non-zero:
  66.  *    Set the bit in all slots.
  67.  *
  68.  * When storing into a slot, if the save level is non-zero:
  69.  *    If the bit isn't set, save the address and contents of the slot
  70.  *      on the current contents chain.
  71.  *    Set the bit after storing the new value.
  72.  *
  73.  * To do a save:
  74.  *    If the save level is non-zero:
  75.  *        Reset the bit in all slots on the contents chain, and in all
  76.  *          objects created since the previous save.
  77.  *    Push the head of contents chain, and reset the chain to empty.
  78.  *
  79.  * To do a restore:
  80.  *    Check all the stacks to make sure they don't contain references
  81.  *      to objects created since the save.
  82.  *    Restore all the slots on the contents chain.
  83.  *    Pop the contents chain head.
  84.  *    If the save level is now non-zero:
  85.  *        Scan the newly restored contents chain, and set the bit in all
  86.  *          the slots it references.
  87.  *        Scan all objects created since the previous save, and set the
  88.  *          bit in all the slots of each object.
  89.  */
  90.  
  91. /* Declare the mask for checking stores. */
  92. /* This is -1 if we are not in a save, 0 if we are in a save. */
  93. int alloc_save_test_mask;
  94. /* Declare the mask for tagging new objects. */
  95. /* This is 0 if we are not in a save, l_new if we are in a save. */
  96. int alloc_save_new_mask;
  97. #define set_in_save()\
  98.   (alloc_save_test_mask = 0, alloc_save_new_mask = l_new)
  99. #define set_not_in_save()\
  100.   (alloc_save_test_mask = -1, alloc_save_new_mask = 0)
  101.  
  102. /* Structure for saved change chain for save/restore. */
  103. /* If where = 0, contents is a t_array that refers to */
  104. /* a newly allocated object; if where = 0 and contents.value.refs is 0, */
  105. /* this is a free record (possible since we allocate them two at a time.) */
  106. /* We merge adjacent objects to reduce */
  107. /* the need to allocate alloc_change records. */
  108. struct alloc_change_s {
  109.     alloc_change *next;
  110.     ref *where;
  111.     ref contents;
  112. };
  113. #define alloc_change_is_free(cp)\
  114.   ((cp)->where == 0 && r_size(&(cp)->contents) == 0)
  115.  
  116. /*
  117.  * Macro to allocate a pair of change records.
  118.  * Must be used in the following form:
  119.  *    if_not_alloc_change_pair(cp, ap, cname)
  120.  *     { ... failure code ...
  121.  *     }
  122.  */
  123. #define if_not_alloc_change_pair(cp, ap, cname)\
  124.   cp = (alloc_change *)alloc(2, sizeof(alloc_change), cname);\
  125.   if ( cp != 0 )\
  126.    { cp->next = ap->changes;\
  127.      cp[1].next = cp;\
  128.      cp[1].where = 0;\
  129.      cp[1].contents.value.refs = 0;\
  130.      r_set_size(&cp[1].contents, 0);\
  131.      ap->changes = cp + 1;\
  132.    }\
  133.   else
  134.  
  135. /* Saved state of allocator and other things as needed. */
  136. struct alloc_save_s {
  137.     alloc_state state;
  138.     alloc_state_ptr cap;
  139.     uint name_cnt;
  140. };
  141.  
  142. /* Debugging printout */
  143. #ifdef DEBUG
  144. private void
  145. alloc_save_print(alloc_change *cp)
  146. {    dprintf1(" %lx:", (ulong)cp);
  147.     if ( cp->where )
  148.       dprintf4(" %lx: %x %x %lx\n", (ulong)cp->where,
  149.            r_type_attrs(&cp->contents), r_size(&cp->contents),
  150.            (ulong)cp->contents.value.intval);
  151.     else
  152.       dprintf2(" %lx(%u)\n", (ulong)cp->contents.value.refs,
  153.            r_size(&cp->contents));
  154. }
  155. #endif
  156.  
  157. /* Forward references */
  158. private void restore_resources(P1(alloc_save *));
  159. private void restore_free(P1(alloc_state_ptr));
  160. private void save_set_new(P2(alloc_state_ptr, int));
  161.  
  162. /* Initialize the save/restore machinery. */
  163. void
  164. alloc_save_init(void)
  165. {    set_not_in_save();
  166. }
  167.  
  168. /* Save the state. */
  169. alloc_save *
  170. alloc_save_state(void)
  171. {    register alloc_state_ptr ap = alloc_state_current;
  172.     alloc_save *save;
  173.     save = (alloc_save *)alloc(1, sizeof(alloc_save),
  174.                    "alloc_save_state");
  175.     if ( save == 0 ) return 0;
  176.     save->state = *ap;
  177.     save->cap = ap;
  178.     save->name_cnt = name_count();
  179.     /* Reset the l_new attribute in all slots.  The only slots that */
  180.     /* can have the attribute set are the ones on the changes chain. */
  181.     save_set_new(ap, 0);
  182.     /* Clear the free chains, to prevent old objects from being freed. */
  183.     memset(&ap->free[0], 0, num_free_chains * sizeof(char *));
  184.     ap->malloc_chain = 0;
  185.     ap->saved = save;
  186.     ap->save_level++;
  187.     ap->changes = 0;
  188.     ap->saved_cbot = ap->cbot;
  189.     ap->saved_ctop = ap->ctop;
  190.     /* Clear the last_freed cache, because the cache pointer */
  191.     /* must point to a chunk at the current save level. */
  192.     ap->last_freed = 0;
  193.     if_debug3('u', "[u]save at %lx: cbot=%lx ctop=%lx\n", (ulong)save,
  194.           (ulong)ap->cbot, (ulong)ap->ctop);
  195.     set_in_save();
  196.     /* Notify the file machinery we just did a save. */
  197.     file_save();
  198.     return save;
  199. }
  200.  
  201. /* Allocate an array of refs and record it as new. */
  202. int
  203. alloc_array(ref *parr, uint attrs, uint num_refs, const char *client_name)
  204. {    register alloc_state_ptr ap = alloc_state_current;
  205.     register ref *obj = (ref *)alloc(num_refs, sizeof(ref), client_name);
  206.     if ( obj == 0 )
  207.         return_error(e_VMerror);
  208.     if ( ap->save_level != 0 && num_refs != 0 )
  209.     {    /* We are saving, record the allocation. */
  210.         register alloc_change *cp = ap->changes;
  211.         if ( cp != 0 && cp->where == 0 &&
  212.              obj == cp->contents.value.refs + r_size(&cp->contents) &&
  213.             /* Don't create a single block that is large enough */
  214.             /* to mislead the allocator into thinking it was */
  215.             /* allocated with a single malloc. */
  216.              r_size(&cp->contents) + num_refs < ap->big_size / sizeof(ref)
  217.            )
  218.         {    /* Merge adjacent allocations. */
  219.             r_inc_size(&cp->contents, num_refs);
  220. #ifdef DEBUG
  221. if ( gs_debug['U'] )
  222.    {    dprintf1("[u]alloc_array(%s) merge", client_name);
  223.     alloc_save_print(cp);
  224.    }
  225. #endif
  226.         }
  227.         else
  228.         {    if ( cp == 0 || !alloc_change_is_free(cp) )
  229.            {    /* Allocate a pair of entries. */
  230.             if_not_alloc_change_pair(cp, ap, "alloc_array(change pair)")
  231.                {
  232.                 alloc_free((char *)obj, num_refs, sizeof(ref),
  233.                        client_name);
  234.                 return_error(e_VMerror);
  235.                }
  236.            }
  237.             cp->where = 0;
  238.             r_set_size(&cp->contents, num_refs);
  239.             cp->contents.value.refs = obj;
  240. #ifdef DEBUG
  241. if ( gs_debug['U'] )
  242.    {    dprintf1("[u]alloc_array(%s)", client_name);
  243.     alloc_save_print(cp);
  244.    }
  245. #endif
  246.         }
  247.     }
  248.     make_array(parr, attrs | ap->local_attr, num_refs, obj);
  249.     return 0;
  250. }
  251.  
  252. /* Deallocate an array of refs.  Only do this if the object */
  253. /* was allocated at the current save level, and we can remove */
  254. /* the save record for the allocation cleanly. */
  255. void
  256. alloc_free_array(ref *parr, const char *client_name)
  257. {    ref *ptr = parr->value.refs;
  258.     uint num_refs = r_size(parr);
  259.     register alloc_state_ptr ap = alloc_state_current;
  260.     alloc_change *cp = ap->changes;
  261.     ref *top = ptr + num_refs;
  262.     if_debug3('U', "[u]alloc_free_array(%s) (%lx,%d)\n",
  263.           client_name, (ulong)ptr, num_refs);
  264.     if ( num_refs == 0 )    /* may point anywhere! */
  265.       return;
  266.     if ( ap->save_level == 0 )
  267.     {    /* Always OK to free if not saving. */
  268.         if_debug0('U', "[u]... not saving\n");
  269.         alloc_free((char *)ptr, num_refs, sizeof(ref),
  270.                client_name);
  271.         return;
  272.     }
  273.     /* Search the save chain for the allocation event. */
  274.     while ( cp != 0 )
  275.     {    ref *rbot;
  276.         if ( cp->where == 0 &&
  277.              ((rbot = cp->contents.value.refs) == ptr ||
  278.               rbot + r_size(&cp->contents) == top)
  279.            )
  280.         {    /* We can undo the allocation record cleanly. */
  281.             if_debug0('U', "[u]... succeeded\n");
  282.             r_inc_size(&cp->contents, -num_refs);
  283.             if ( rbot == ptr )
  284.                 cp->contents.value.refs = top;
  285.             alloc_free((char *)ptr, num_refs, sizeof(ref),
  286.                    client_name);
  287.             break;
  288.         }
  289.         cp = cp->next;
  290.     }
  291. }
  292.  
  293.  
  294. /* Record a state change that must be undone for restore, */
  295. /* and mark it as having been saved. */
  296. /* This can only be called if we are in a save. */
  297. int
  298. alloc_save_change(ref *where, const char *client_name)
  299. {    register alloc_state_ptr ap = alloc_state_current;
  300.     register alloc_change *cp;
  301.     if ( ap->save_level == 0 ) return 0;    /* no saving */
  302.     cp = ap->changes;
  303.     if ( cp == 0 || !alloc_change_is_free(cp) )
  304.        {    /* Allocate a pair of entries. */
  305.         if_not_alloc_change_pair(cp, ap, "alloc_save_change(change pair)")
  306.            {    return -1;
  307.            }
  308.        }
  309.     cp->where = where;
  310.     ref_assign(&cp->contents, where);
  311. #ifdef DEBUG
  312. if ( gs_debug['U'] )
  313.    {    dprintf1("[u]save(%s)", client_name);
  314.     alloc_save_print(cp);
  315.    }
  316. #endif
  317.     if ( !r_is_packed(where) ) r_set_attrs(where, l_new);
  318.     return 0;
  319. }
  320.  
  321. /* Return the current save level */
  322. int
  323. alloc_save_level(void)
  324. {    return alloc_state_current->save_level;
  325. }
  326.  
  327. /* Test whether a reference would be invalidated by a restore. */
  328. int
  329. alloc_is_since_save(const char *ptr, const alloc_save *save)
  330. {
  331.     /* A reference can postdate a save in one of three ways: */
  332.     /*    - It is in the chunk that was current at the time */
  333.     /*        of the save, and allocated more recently. */
  334.     /*    - It is in a chunk allocated since the save; */
  335.     /*    - It was malloc'ed since the save; */
  336.  
  337.     register alloc_state_ptr ap = save->cap;
  338.  
  339.     if_debug2('U', "[U]is_since_save %lx, %lx:\n",
  340.           (ulong)ptr, (ulong)save);
  341.  
  342.     /* Check against current chunk at the time of the save */
  343.     if ( ptr_is_in_chunk(ptr, &save->state.current) )
  344.        {    /* In the chunk, check against allocation pointers */
  345.         /* at the time of the save */
  346.         if_debug2('U', "[U?]  current chunk %lx, %lx\n",
  347.              (ulong)save->state.cbot, (ulong)save->state.ctop);
  348.         return ( (ptr_ord_t)ptr >= (ptr_ord_t)save->state.cbot &&
  349.              (ptr_ord_t)ptr < (ptr_ord_t)save->state.ctop );
  350.        }
  351.  
  352.     /* Check against chunks allocated since the save */
  353.        {    const alloc_chunk *chunk = &ap->current;
  354.         while ( chunk->save_level > save->state.save_level )
  355.            {    if ( ptr_is_in_chunk(ptr, chunk) )
  356.                {    if_debug3('U', "[U+]  new chunk %lx: %lx, %lx\n", chunk,
  357.                       (ulong)chunk->base, (ulong)chunk->limit);
  358.                 return 1;
  359.                }
  360.             chunk = chunk->next;
  361.            }
  362.        }
  363.  
  364.     /* Check the malloc chains since the save */
  365.        {    const alloc_state *asp = ap;
  366.         for ( ; asp != &save->state; asp = &asp->saved->state )
  367.            {    const alloc_block *mblk = asp->malloc_chain;
  368.             for ( ; mblk != 0; mblk = mblk->next )
  369.               if ( alloc_block_size + (char *)mblk == ptr )
  370.                {    if_debug0('U', "[U+]  malloc'ed\n");
  371.                 return 1;
  372.                }
  373.            }
  374.        }
  375.  
  376.     /* Not in any of those places, must be OK. */
  377.     return 0;
  378. }
  379.  
  380. /* Test whether a name would be invalidated by a restore. */
  381. int
  382. alloc_name_is_since_save(const ref *pnref, const alloc_save *save)
  383. {    return name_is_since_count(pnref, save->name_cnt);
  384. }
  385.  
  386. /* Validate a saved state pointer. */
  387. int
  388. alloc_restore_state_check(const alloc_save *save)
  389. {    const alloc_save *sprev = save->cap->saved;
  390.     while ( sprev != save )
  391.        {    if ( sprev == 0 ) return -1;    /* not on chain */
  392.         sprev = sprev->state.saved;
  393.        }
  394.     return 0;
  395. }
  396.  
  397. /* Restore the state.  The client is responsible for calling */
  398. /* alloc_restore_state_check first, and for ensuring that */
  399. /* there are no surviving pointers for which alloc_is_since_save is true. */
  400. void
  401. alloc_restore_state(alloc_save *save)
  402. {    register alloc_state_ptr ap = save->cap;
  403.     alloc_save *sprev;
  404.  
  405.     if_debug1('u', "[u]restore from %lx\n", (ulong)save);
  406.  
  407.     /* Iteratively restore the state */
  408.     do
  409.       { sprev = ap->saved;
  410.  
  411.         /* Release resources other than memory */
  412.         restore_resources(sprev);
  413.  
  414.         /* Undo changes since the save. */
  415.         { alloc_change *cp = ap->changes;
  416.           while ( cp )
  417.         {
  418. #ifdef DEBUG
  419. if ( gs_debug['U'] )
  420.    {        dprintf("[U]restore");
  421.         alloc_save_print(cp);
  422.    }
  423. #endif
  424.           if ( cp->where )
  425.             ref_assign(cp->where, &cp->contents);
  426.           else if ( r_size(&cp->contents) != 0 )    /* might be an unfilled save record */
  427.             { alloc_free((char *)cp->contents.value.refs,
  428.                  r_size(&cp->contents), sizeof(ref),
  429.                  "alloc_restore_state");
  430.             }
  431.           cp = cp->next;
  432.         }
  433.         }
  434.  
  435.         /* Free memory allocated since the save. */
  436.         restore_free(ap);
  437.  
  438.         /* Restore the allocator state. */
  439.         *ap = sprev->state;
  440.         alloc_free((char *)sprev, 1, sizeof(alloc_save),
  441.                "alloc_restore_state(alloc_save)");
  442.  
  443.       }
  444.     while ( sprev != save );
  445.  
  446.     /* Clean up */
  447.     sprev = ap->saved;
  448.     if ( sprev == 0 )
  449.         ap->saved_cbot = ap->saved_ctop = 0;
  450.     else
  451.         ap->saved_cbot = sprev->state.cbot,
  452.         ap->saved_ctop = sprev->state.ctop;
  453.     /* Clear the last_freed cache, because the cache pointer */
  454.     /* must point to a chunk at the current save level. */
  455.     ap->last_freed = 0;
  456.     if ( ap->save_level == 0 )
  457.         set_not_in_save();
  458.     /* Set the l_new attribute in all slots that have been saved. */
  459.     save_set_new(ap, l_new);
  460. }
  461.  
  462. /* Restore to the initial state, releasing all resources. */
  463. /* The allocator is no longer usable after calling this routine! */
  464. void
  465. alloc_restore_all(void)
  466. {    register alloc_state_ptr ap = alloc_state_current;
  467.  
  468.     /* Restore to a state outside any saves. */
  469.     while ( ap->saved != 0 )
  470.         alloc_restore_state(ap->saved);
  471.  
  472.     /* Release resources other than memory, using */
  473.     /* a fake save object that has no associated memory. */
  474.     {    alloc_save empty_save;
  475. #if arch_ptrs_are_signed
  476. #  define min_ptr (-1L << (sizeof(char *) * 8 - 1))
  477. #else
  478. #  define min_ptr 0L
  479. #endif
  480. #define max_ptr (~min_ptr)
  481.  
  482.         empty_save.state.current.base =
  483.             empty_save.state.cbot = (byte *)min_ptr;
  484.         empty_save.state.current.limit =
  485.             empty_save.state.ctop = (byte *)max_ptr;
  486.         empty_save.name_cnt = name_count();    /* don't bother to release */
  487.  
  488.         restore_resources(&empty_save);
  489.     }
  490.  
  491.     /* Finally, release memory. */
  492.     restore_free(ap);
  493. }
  494.  
  495. /* Release resources for a restore */
  496. private void
  497. restore_resources(alloc_save *sprev)
  498. {
  499.         /* Close inaccessible files. */
  500.         file_restore(sprev);
  501.  
  502.         /* Remove entries from font and character caches. */
  503.         font_restore(sprev);
  504.  
  505.         /* Adjust the name table. */
  506.         name_restore(sprev->name_cnt);
  507. }
  508.  
  509. /* Release memory for a restore. */
  510. private void
  511. restore_free(alloc_state_ptr ap)
  512. {
  513.         /* Free chunks allocated since the save. */
  514.         { alloc_chunk *cp = ap->current_ptr;
  515.           *cp = ap->current;    /* update in memory */
  516.         }
  517.         while ( ap->current.save_level == ap->save_level )
  518.           {    byte *cp = (byte *)ap->current_ptr;
  519.         uint csize = ap->climit - cp;
  520.         ap->current_ptr = ap->current.next;
  521.         if ( ap->current_ptr != 0 )
  522.             ap->current = *ap->current_ptr;
  523.         else        /* only possible from restore_all */
  524.             ap->current.save_level--;
  525.         (*ap->mprocs->free)((char *)cp, 1, csize, "alloc_restore_state(chunk)");
  526.           }
  527.  
  528.         /* Free blocks allocated with malloc since the save. */
  529.         /* Since we reset the chain when we did the save, */
  530.         /* we just free all the objects on the current chain. */
  531.         { while ( ap->malloc_chain != 0 )
  532.         { alloc_block *mblock = ap->malloc_chain;
  533.           ap->malloc_chain = mblock->next;
  534.           (*ap->mprocs->free)((char *)mblock,
  535.                    1, alloc_block_size + mblock->size,
  536.                    "alloc_restore_state(malloc'ed)");
  537.         }
  538.         }
  539. }
  540.  
  541. /* ------ Internal routines ------ */
  542.  
  543. /* Set or reset the l_new attribute in every slot on the current */
  544. /* change chain. */
  545. private void
  546. save_set_new(alloc_state_ptr ap, int new)        /* l_new or 0 */
  547. {    register alloc_change *cp = ap->changes;
  548.     for ( ; cp; cp = cp->next )
  549.     {    ref *rp = cp->where;
  550.         if ( rp != 0 )
  551.         {    if ( !r_is_packed(rp) )
  552.                 rp->tas.type_attrs =
  553.                   (rp->tas.type_attrs & ~l_new) + new;
  554.         }
  555.         else
  556.         {    register ushort size = r_size(&cp->contents);
  557.             if ( size )
  558.             {    register ref *ep = cp->contents.value.refs;
  559.                 if ( new )
  560.                     do
  561.                     {    if ( !r_is_packed(ep) )
  562.                           ep->tas.type_attrs |= l_new;
  563.                         ep++;
  564.                     }
  565.                     while ( --size );
  566.                 else
  567.                     do
  568.                     {    if ( !r_is_packed(ep) )
  569.                           ep->tas.type_attrs &= ~l_new;
  570.                         ep++;
  571.                     }
  572.                     while ( --size );
  573.             }
  574.         }
  575.     }
  576. }
  577.